package edu.colorado.cs

import akka.actor.{Actor, ActorRegistry}
import akka.actor.Actor._
import akka.event.EventHandler

object Server {
  private var project: Option[Project] = None

  // This function starts the server on the given port and starts the
  // class file server on the next port.
  def start(port: Int) = {
    EventHandler.info(this, "Starting server on port " + port)
    EventHandler.info(this, "Starting class server on port " + (port + 1))

    // This line starts the ClassFileServer (written by Sun) that serves
    // the ProjectInfo class to clients. The second parameter is the
    // classpath the server should be looking for class files in.
    //
    // TODO: Is it safe to just do port + 1?
    new ClassFileServer(port + 1, "project_info")

    EventHandler.info(this, "Class server started.")

    // The following lines start the remote actor listening on the
    // specified port and register the actor so clients can get handles
    // to it. By using remote.registerPerSession, each client gets its
    // own "copy" of the actor.
    remote.start("localhost", port)
    remote.registerPerSession("actor", actorOf[ServerActor])

    EventHandler.info(this, "Server started.")
  }

  // This function sets which project the server is running.
  def set_project(p: Project) = {
    project match {
      case None => {
        EventHandler.info(this, "Setting project.")
        p.init_data()
        project = Some(p)
      }
      case Some(x: Project) => {
        EventHandler.warning(this, "Project already exists.")
      }
    }
  }

  // This function returns the project the server is currently using,
  // or None if the server hasn't had a project defined.
  def get_project(): Option[Project] = project

  // This class defines the remote actor.
  class ServerActor extends Actor {
    def receive() = {
      // Received a request for data.
      case GetData() => {
        EventHandler.debug(this, "Data requested.")
        // Check that the project has been defined.
        Server.get_project() match {
          case Some(p: Project) => {
            // If the project has been defined, try to get data.
            p.split_data() match {
              case Some(d: Double) => {
                // If there is data, send it in a Data message.
                EventHandler.debug(this, "Replying with data: " + d)
                self reply Data(d)
              }
              case None => {
                // If there isn't data, send a NoData message, then
                // recombine the data.
                EventHandler.debug(this, "No data to reply with.")
                self reply NoData()
                Server.get_project() match {
                  case Some(p: Project) => {
                    p.recombine_data()
                  }
                }
              }
            }
          }
          case None => {
            // If the project hasn't been set, break.
            EventHandler.error(this, "Server project not set.")
            System.exit(1)
          }
        }
      }
      // Received a result from a client.
      case Result(in: Double, out: Double) => {
        EventHandler.debug(this, "Receiving result: " + out)
        // Check that a project has been defined.
        Server.get_project() match {
          case Some(p: Project) => {
            // If a project has been defined, add the result to it.
            EventHandler.debug(this, "Adding result.")
            p.add_result(in, out)
          }
          case None => {
            // If a project hasn't been defined, break.
            EventHandler.error(this, "Server project not set.")
            System.exit(1)
          }
        }
      }
    }
  }
}
